#include "Request.h"
#include "GetRequest.h"
#include "ConnectRequest.h"
#include "DeleteRequest.h"
#include "HeadRequest.h"
#include "OptionsRequest.h"
#include "PostRequest.h"
#include "PutRequest.h"
#include "TraceRequest.h"
#include "Codes.h"
#include <stdlib.h>
#include <sstream>

Request::Request() {
    method = "";
    uri = "";
    version = "HTTP/1.1";
}

Request::Request(stringstream& s) { // need to break the string into sections.
    //Get Method, URI, and Version
    char u[1024], v[255];
    int nbrRead;
    string temp;
    if (!s.eof() && s.peek() > 0) {
        getline(s, temp);
        nbrRead = sscanf(temp.c_str(), "%s %s\r\n", &u, &v);
        uri = u;
        version = v;
        char fieldName[1024], fieldValue[1024];
        map<string,bool> requestHeaderFieldsHas = GetRequestHeaderFieldMap();
        // cout << method << uri << version;
        //Get All Header Fields
        while (temp != "\r") {
            if(!s.eof() && s.peek() > 0)
            {
                getline(s, temp);
                sscanf(temp.c_str(), "%[A-Za-z-]: %[^\r\n]\r\n", fieldName, fieldValue);
                string testForValidField = string(fieldName);
                if(requestHeaderFieldsHas[testForValidField])
                {
                        headers.insert(pair<string, string> (fieldName, fieldValue));
                }
                else
                {
                    cerr << "Invalid request header field." << endl;
                    exit(-1);
                }
            }
            else
            {
                cerr << "No more data in stream" << endl;
                break;
            }
        }
    }
    else {
        cerr << "No more data in stream." << endl;
    }

}

Request::Request(Request &orig) {
    method = orig.method;
    uri = orig.uri;
    version = orig.version;
    headers = orig.headers; // May need to iterate through headers and add in items.
}

Request::~Request() {
}

void Request::PrintInfo() {
    cout << "Method: " << method << "\r\nURI: " << uri << "\r\nVersion: " << version << endl;
    map<string, string>::iterator it;
    for (it = headers.begin(); it != headers.end(); it++) {
        cout << (*it).first << " => " << (*it).second << endl;
    }
}

string Request::GetMethod()
{
    return this->method;
}

string Request::GetUri()
{
    return this->uri;
}

map<string,bool> Request::GetRequestHeaderFieldMap()
{
    map<string,bool> requestHeaderFields;
    //Request Header Fields
    requestHeaderFields.insert(pair<string,bool>("Accept",true));
    requestHeaderFields.insert(pair<string,bool>("Accept-Charset",true));
    requestHeaderFields.insert(pair<string,bool>("Accept-Encoding",true));
    requestHeaderFields.insert(pair<string,bool>("Accept-Language",true));
    requestHeaderFields.insert(pair<string,bool>("Authorization",true));
    requestHeaderFields.insert(pair<string,bool>("Expect",true));
    requestHeaderFields.insert(pair<string,bool>("From",true));
    requestHeaderFields.insert(pair<string,bool>("Host",true));
    requestHeaderFields.insert(pair<string,bool>("If-Match",true));
    requestHeaderFields.insert(pair<string,bool>("If-Modified-Since",true));
    requestHeaderFields.insert(pair<string,bool>("If-None-Match",true));
    requestHeaderFields.insert(pair<string,bool>("If-Range",true));
    requestHeaderFields.insert(pair<string,bool>("If-Unmodified-Since",true));
    requestHeaderFields.insert(pair<string,bool>("Max-Forwards",true));
    requestHeaderFields.insert(pair<string,bool>("Proxy-Authorization",true));
    requestHeaderFields.insert(pair<string,bool>("Range",true));
    requestHeaderFields.insert(pair<string,bool>("Referer",true));
    requestHeaderFields.insert(pair<string,bool>("TE",true));
    requestHeaderFields.insert(pair<string,bool>("User-Agent",true));
    //General Header Fields
    requestHeaderFields.insert(pair<string,bool>("Cache-Control",true));
    requestHeaderFields.insert(pair<string,bool>("Connection",true));
    requestHeaderFields.insert(pair<string,bool>("Date",true));
    requestHeaderFields.insert(pair<string,bool>("Pragma",true));
    requestHeaderFields.insert(pair<string,bool>("Trailer",true));
    requestHeaderFields.insert(pair<string,bool>("Transfer-Encoding",true));
    requestHeaderFields.insert(pair<string,bool>("Upgrade",true));
    requestHeaderFields.insert(pair<string,bool>("Via",true));
    requestHeaderFields.insert(pair<string,bool>("Warning",true));
    //Entity Header Fields
    requestHeaderFields.insert(pair<string,bool>("Allow",true));
    requestHeaderFields.insert(pair<string,bool>("Content-Encoding",true));
    requestHeaderFields.insert(pair<string,bool>("Content-Language",true));
    requestHeaderFields.insert(pair<string,bool>("Content-Length",true));
    requestHeaderFields.insert(pair<string,bool>("Content-Location",true));
    requestHeaderFields.insert(pair<string,bool>("Content-MD5",true));
    requestHeaderFields.insert(pair<string,bool>("Content-Range",true));
    requestHeaderFields.insert(pair<string,bool>("Content-Type",true));
    requestHeaderFields.insert(pair<string,bool>("Expires",true));
    requestHeaderFields.insert(pair<string,bool>("Last-Modified",true));
    return requestHeaderFields;
}

Request * Request::Create(stringstream& s) {
    string method;
    s >> skipws >> method;

    try {
        if (method == "GET") {
            return new GetRequest(s);
        } else if (method == "OPTIONS") //Make a case for all different request methods
        {
            return new OptionsRequest(s);
        } else if (method == "PUT") {
            return new PutRequest(s);
        } else if (method == "HEAD") {
            return new PutRequest(s);
        } else if (method == "POST") {
            return new PostRequest(s);
        } else if (method == "DELETE") {
            return new DeleteRequest(s);
        } else if (method == "TRACE") {
            return new TraceRequest(s);
        } else if (method == "CONNECT") {
            return new ConnectRequest(s);
        } else {
            cerr << "Not a valid method." << endl;
            throw BAD_REQUEST;
        }
    } catch (int e) {
        cerr << e << endl;
        exit(-1);
    }
    //else return error or output error and start over????
}

